﻿/**
 * 事件模块 - 核心模块 -- 可选
 * 
 * 将事件相关操作都集中在这里绑定，方便管理
 * 
 */
(function(E, $){
var Consts = E.consts, timeids=[], Event,
	// 事件默认监控对象是编辑器
	events = [
		// 编辑器自定义事件
		// 使用 on 开头
		'onready',    // 编辑器页面dom加载完成时，仅执行一次
		
		'oncreate',   // 编辑器内容区域创建完成时
		'oncomplete', // 编辑器的工具栏出现和编辑区域事件都已绑定时
		
		'onafteraction',  // 执行插件后
		'onmonitor',   // 编辑器内容发生变化时
		'onredo',   // 编辑器内容发生重做时
		'onundo',   // 编辑器内容发生撤销时
		
		// scroll 事件监控对象是编辑器所在页面
		'onscroll',
		
		// 编辑器编辑器区域标准事件
		// 不使用 on 开头
		'mouseover','mousedown','mouseup','click','contextmenu',
		'keyup','keydown','paste','beforepaste','load',
		'dragenter','dragover','drop','dragleave',
		
		'dragend'
	];

/**
 * 给编辑器增加事件机制，模仿浏览器的事件机制。
 * 通过事件机制解耦编辑器核心和模块之间的通信问题。
 * 这种事件机制被称之为观察者模式。
 * 
 * 参考：《JavaScript高级程序设计(第2版)》第18.3节：自定义事件
 * 推荐：在此强烈推荐这边书！
 */

E.mod("Event", {
	getElement: function(){
		return iframeEvent.element[E.eid];
	},
	
	getTarget: function(){
		var element = this.getElement();
		if(element){
			return element.target;
		}
	},
	
	setElement : function(target) {
		iframeEvent.saveCursorElem( target );
	},
	
    handlers : {},
	
	// 注册编辑器事件
	bind: function(type, handler){
		if( $.inArray(type, events) === -1 ){
			return E.log('warning', '编辑器不允许注册'+type+'类型的事件');
		}
		
		if( type.substr(0,2) === 'on' ){
			// 自定义事件
			if (typeof this.handlers[type] === "undefined"){
				this.handlers[type] = [];
			}
			this.handlers[type].push(handler);
		}else{
			// 标准事件
			iframeEvent.bind( type, handler );
		}
		
		E.log('run', '注册编辑器'+type+'类型事件');
	},
	
	// 触发编辑器事件
	trigger: function(event){
		if( $.inArray(event.type, events) === -1 ){
			return E.log('warning', '编辑器没有注册'+event.type+'类型的事件');
		}
		if (this.handlers[event.type] instanceof Array){
			
			var handlers = this.handlers[event.type];
				//将编辑器option添加到事件当中，方便使用
				event.option = E.get('option');
				event.dom = E.get('document');
				
			E.log('event', '触发事件'+event.type+'，存在个'+ handlers.length +'注册事件');
			
			for (var i=0, len=handlers.length; i < len; i++){
				handlers[i](event);
			}
		}
	},
	
	// 删除编辑器事件
	remove: function(type, handler){
		if (this.handlers[type] instanceof Array){
			var handlers = this.handlers[type];
			for (var i=0, len=handlers.length; i < len; i++){
				if (handlers[i] === handler){
					break;
				}
			}
			
			handlers.splice(i, 1);
		}
	}
});
Event = E.Event = E.mod('Event');

/**
 * 给编辑区域绑定点击等事件，
 * 被 E.create() 方法调用。
 */
Event.bind("oncreate", function(){
	var evt = iframeEvent;
	evt.mousedown( );
	evt.mouseup( );
	evt.click( );
//	evt.mouseover();//触发太频繁，不推荐使用
	evt.contextmenu( );
	evt.keyup( );
	evt.keydown();
	evt.paste();
	
	evt.dragenter();
	evt.dragover();
	evt.drop();
	evt.dragleave();
	
	evt.dragend();
	
	evt.load();
	
});


/**
 * 编辑器准备工作，在document.ready时调用，仅执行一次，
 * 被 E.ready() 方法调用。
 */
Event.bind('onready', function(){
	MainEvent.click();
	MainEvent.scroll();
});

// 如果不是IE浏览器则禁用下面的插件
if( !Consts.IE ){
	Event.bind('oncomplete', function(){
		E.plugin('Cut').disable();
		E.plugin('Copy').disable();
		E.plugin('Paste').disable();
	});
	
}
/**
 * 编辑器所在页面 document.ready 执行一次
 */
var MainEvent = {
	click: function(){
		//提示：IE下不能使用$(window)，使用$(document)较好；
		
		//在firefox下右键点击会触发click事件，其他浏览器则右键不触发click事件，
		//为了保持一致性，这里针对firefox处理一下;
		function onclick( e ){
			var target = $(e.target);
			
			if( (Consts.mozilla && e.button !== 0)
				|| target.closest('.jqe-menu').size() 
				|| target.closest('.jqe-panel').size()
				|| target.closest('.jqe-toolbar').size()
				|| target.closest('.jqe-panel-overlay').size()
			){
				return true;
			}
			
			//如果点击的不是按钮、菜单、面板，隐藏菜单和面板
			if( $(".jqe-menu:visible").size() || $(".jqe-panel:visible").size() ){
				E.hidePanel();
			}
			// 这里不能return false，也不需要return false
			// 否则将导致页面上的其他操作不能正常执行，如复选框的选择等
			//return false;
		}
		
		// 只有使用mousedown事件，才可以在滚动条点击触发关闭面板，click mouseup都不行
		Consts.IE ? $(document).mousedown(onclick) : 
			$(window).mousedown(onclick);
		
	},
	
	scroll: function(){
		$(window).scroll(function( e ){
			E.Event.trigger( {type:'onscroll', event: e} );
		});
	}
};


/**
 * 创建iframe编辑区域时执行，给iframe.document绑定事件
 */
var iframeEvent = {
	/**
	element:
		光标点击处的元素
		eid->{
			event->event,
			table->table,
			row->row,
			cell->cell,
			a->a,
			img->img,
			h2->h2,
			h3->h3
		}
	*/
	element : {},
	
	
	/**
	 * 被 iframe 编辑区域增加事件，
	 * @param type string
	 * 		事件类型，如click、mousedown、contextmenu 等
	 * @param fn function
	 * 		事件处理方法，同一个类型的事件可以添加多个处理方法
	 */
	bind: function( type, fn ){
		var self = this, fns = self[type+'s'];
		if( $.isArray(fns) ){
			fns.push(fn);
		}
		return self;
	},
	
	//初始化，iframeEvent对象下面紧接着就会调用
	init: function( ){
		var self = this;
		self.extend('mouseover').extend('mousedown').extend('mouseup').extend('click').extend('contextmenu').extend('keyup').extend('keydown').extend('paste').extend('load');
		
		// 实现图片拖放上传效果事件
		self.extend('dragenter').extend('dragover').extend('drop').extend('dragleave');
		
		self.extend('dragend');
		return self;
	},
	
	//扩展 iframeEvent 自身的属性和方法
	extend: function( type){
		var self = this;
		//给 iframeEvent 增加一个属性
		self[type+'s'] = [];
		timeids[type] = 0;
		//给 iframeEvent 增加一个方法
		self[type] = function( ){
			var dom = E.get('document'),
				win = E.get('window'),
				obj = (type == 'load') ? win : dom;
			
			// IE 下的粘帖事件必须绑定到dom.body上，否则不起作用
			if( Consts.IE && type === "paste" ) obj = dom.body;
			if( Consts.IE && type === "beforepaste" ) obj = dom.body;
			
			//Opera貌似怎么都绑定不上粘帖事件，所以也就不能处理粘帖数据
			
			//给当前 iframe dom 绑定对应事件，
			//此事件调用通过 iframeEvent.bind() 方法添加进来的所有处理方法
			$(obj).bind(type, function(e){
				var dom = this, bool = true, fns = self[type+'s'], fn;
				//遍历所有对应方法
				for(var i in fns ){
					fn = fns[i];
					if( typeof fn == 'function' ){
						if( false === fn(dom, e) ){
							bool = false;
						}
					}
				}
				
				//如果有一个事件返回 false ，则终止事件冒泡
				return bool;
			});
		};
		return self;
	},
	
	// 记录光标处的元素
	saveCursorElem: function( target ){
		// 初始化 this.element
		var eid=E.eid,
			element = this.element[eid] = {target: target[0]};
		
		var a = target.closest('a'),
			img = target.closest('img'),
			h2 = target.closest('h2'),
			h3 = target.closest('h3'),
			pre = target.closest('pre'),
			sub = target.closest('sub'),
			sup = target.closest('sup'),
			cell = target.closest('td,th'),
			row = cell.closest('tr'),
			table = row.closest('table'),
			ol = target.closest('ol'),
			ul = target.closest('ul'),
			embed = target.closest('embed');
		
		// 潘雪鹏 2011-07-01 13:31
		// 这种方式可能会出现错误：Invalid left-hand side in assignment
		// 为什么呢？
		
		// a.size() && element.a = a;
		// ...
		
		// 因为 && 符号的优先级比 = 高，使用()后解决问题
		// 也可以使用if语句替换
		a.size() && (element.a = a[0]);
		img.size() && (element.img = img[0]);
		h2.size() && (element.h2 = h2[0]);
		h3.size() && (element.h3 = h3[0]);
		pre.size() && (element.pre = pre[0]);
		sub.size() && (element.sub = sub[0]);
		sup.size() && (element.sup = sup[0]);
		cell.size() && (element.cell = cell[0]);
		row.size() && (element.row = row[0]);
		table.size() && (element.table = table[0]);
		embed.size() && (element.embed = embed[0]);
		ol.size() && (element.ol = ol[0]);
		ul.size() && (element.ul = ul[0]);
		
		E.log("run", "记录光标处元素"+target[0].nodeName);
	}
};

iframeEvent.init();


//鼠标移到到编辑区域时触发，将当前eid设置给E.eid
iframeEvent.bind('mouseover', function(dom, e){
	var eid = $('body', dom).attr('id');
	E.setEid(eid);
});

//鼠标左中右键按下时都会触发mousedown事件
iframeEvent.bind('mousedown', function(dom, e){
	var target = $(e.target);
	
	iframeEvent.saveCursorElem( target );
});

//鼠标左中右键抬起时都会触发mouseup事件
iframeEvent.bind('mouseup', function(dom, e){
	var eid = $('body', dom).attr('id'), 
		Menu = E.mod('Menu'),
		Selection = E.mod("Selection"),
		Toolbar = E.mod('Toolbar');

	E.setEid(eid);
	
	if(Selection){Selection.saveRange();}
	
	
	if(Toolbar){
		//根据鼠标所在文字的状态更新按钮状态
		//比如鼠标点击被加粗的文字后，将加粗按钮设置为选中状态
		Toolbar.updateState();
	}
	
	//firefox、chrome、oprea、IE9下，e.button左键=0，中键=1，右键=2
	//ie6/7/8下，e.button左键=1，右键=2
	/*
	if( (Consts.IE && e.button !== 1) || (!Consts.IE && e.button !== 0) ){
		return true;
	}
	*/
	
	//显示和mouseup事件相关的快捷菜单
	if(Menu){Menu.set( e, 'mouseup' );}
});

//左键点击事件
//主要用来隐藏面板、显示快捷菜单、以及根据鼠标所在文字的状态更新按钮状态
iframeEvent.bind('click', function(dom, e){
	var target = $(e.target),
		Selection = E.mod("Selection"),
		Menu = E.mod('Menu');
		
	//2011.02.10 21:37
	//firefox下点击右键，同样会触发click事件
	//目前仅发现firefox有这个情况
	//firefox、chrome、oprea下，左键e.button==0，中键==1，右键==2
	if( Consts.mozilla && e.button !== 0 ){
		return true;
	}
	
	if( Selection ){
		//Selection.getText() 返回的是object 对象，需要转为字符串
		//顾使用''+Selection.getText()
		var text = ''+Selection.getText();
	}
	
	//显示和click事件相关的快捷菜单
	if(Menu){Menu.set( e, 'click' );}
	
	// chrome 浏览器下，点击图片不能使图片处于被选中状态
	// 可以通过程序使图片被选中
	// 但是下面这个代码，不能按预期工作（2011-08-01）
	if( Selection ){
		if ($.browser.webkit) {
			if( e.target.nodeName.toLowerCase() === 'img' ){
				var img = e.target, range = dom.createRange();
				range = Selection.getRange();
				range.setStartBefore(img);
				range.setEndAfter(img);
				range.selectNode(img);
				
				// 2011-09-05 加上下面三行就可以工作了
				var sel = Selection.get();
				sel.removeAllRanges();
				sel.addRange(range);
			}
		}
	}
	
	E.log('run', E.eid + ' mouse click');
});

//右键点击事件
//主要用来生成右键菜单
iframeEvent.bind('contextmenu', function(dom, e){
	var target = $(e.target),
		Selection = E.mod("Selection"),
		Menu = E.mod('Menu');
	
	//当编辑器当中有选中文字时，不触发右键事件
	if( Selection ){
		var text = ''+Selection.get('text');
		if( e.ctrlKey && text ){
			return true;
		}
	}
	
	E.hidePanel();
	
	return Menu ? Menu.set( e, 'contextmenu' ) : true;
});

// 键盘抬起事件
iframeEvent.bind('keyup', function(dom, e){
	var Selection = E.mod('Selection'),
		Toolbar = E.mod('Toolbar'),
		elem,
		keyCode = e.keyCode || e.which || e.charCode;
		
	//alert(keyCode);
	if(Selection){
		Selection.saveRange();
	}
	
	if(/^(8|13|37|38|39|40|110)$/.test(keyCode)){
		if(Selection){
			elem = Selection.getElementByCursor();
			//alert(elem.nodeName);
			if( elem ){
				iframeEvent.saveCursorElem( $(elem) );
			}
		}
		
		if(Toolbar){
			//根据鼠标所在文字的状态更新按钮状态
			//比如鼠标点击被加粗的文字后，将加粗按钮设置为选中状态
			Toolbar.updateState();
		}
	}
	
});

Event.bind('keydown', function(dom, e){
	var keyCode = e.keyCode || e.which || e.charCode;
	// 对于非输入性按键不应该触发monitor事件，如上下箭头 shift alt ctrl等等
	var notInputKey = [
		16,17,18,20,27,33,34,35,36,
		37,38,39,40,44,45,91,113,115,118,119,122,144
	];
	
	if( $.inArray(keyCode, notInputKey) > -1
		|| e.ctrlKey
		|| e.altKey
	){
		return true;
	}
	
	clearTimeout(timeids['keydown']);
	timeids['keydown'] = setTimeout(function(){
		E.monitor('keydown');
	}, 1000);
});


iframeEvent.bind('load', function(dom, e){
	E.log('run', E.eid + ' onload');
});

})(jQEditor, jQuery);